///************************************************************************/
///* Overlaps                                                             */
///************************************************************************/

template <typename T>
bool IsOverlapping(const GenericRectangle2D<T>& rect, const GenericCircle2D<T>& circle);
/// TODO: Finish above, and add generic overlapping functions for all shape combinations

///************************************************************************/
///* Positions and distances                                              */
///************************************************************************/

/// //////////////// ///
/// Points and lines ///
/// //////////////// ///

/// Function: GetClosestPositionOnRayToPoint
template <typename T>
T GetClosestPositionOnRayToPoint( const GenericPoint2D<T>& point, const GenericRay2D<T>& ray )
{
	float div = ray.Direction.GetDotProduct( ray.Direction );
	if( div < Math::EpsilonValue<T>() )
		return (T)0;
	else
		return (ray.Direction.GetDotProduct( point ) - ray.Direction.GetDotProduct( ray.Start )) / div;
}

/// Function: GetClosestPointOnRayToPoint
template <typename T>
GenericPoint2D<T> GetClosestPointOnRayToPoint( const GenericPoint2D<T>& point, const GenericRay2D<T>& ray, float* positionOnRay = 0 )
{
	float t = GetClosestPositionOnRayToPoint( point, ray );
	if ( positionOnRay ) *positionOnRay = t;
	return ray.GetPoint(t);
}

/// Function: GetDistanceToLine
template <typename T>
float GetDistanceToLine( const GenericPoint2D<T>& point, const GenericRay2D<T>& ray, float* positionOnRay = 0 )
{
	GenericPoint2D<T> closest = GetClosestPointOnRayToPoint( point, ray, positionOnRay );
	return point.GetDistanceTo(closest);
}

/// Function: GetDistanceSquaredToLine
template <typename T>
float GetDistanceSquaredToLine( const GenericPoint2D<T>& point, const GenericRay2D<T>& ray, float* positionOnRay = 0 )
{
	GenericPoint2D<T> closest = GetClosestPointOnRayToPoint( point, ray, positionOnRay );
	return point.GetDistanceSquaredTo(closest);
}

/// Function: PointMultiplyAdd
template <typename T>
GenericPoint2D<T> PointMultiplyAdd(const GenericPoint2D<T>& start, const GenericPoint2D<T>& dir, float v)
{
	return start + dir*v;
}

/// Function: GetPointOnRay2D
template <typename T>
GenericPoint2D<T> GetPointOnRay2D(const GenericPoint2D<T>& start, const GenericPoint2D<T>& dir, const T& offset)
{
	return start + dir * offset;
}

template <typename T>
GenericPoint2D<T> operator*(const GenericPoint2D<T>& point, const GenericAngle2D<T>& angle)
{
	return GenericPoint2D<T>(point.X*angle.x - point.Y*angle.y, point.X*angle.y + point.Y*angle.x);
}

/// ///////////////////// ///
/// Rectangles and shapes ///
/// ///////////////////// ///

/// Function: CenterOnWhileInBounds
/// Moves the `view` rectangle so that it is centered on the `point`, or at least the points is inside it, if the
/// `bounds` rectangle doesn't allow for it.
/// If `points` isn't in `bounds`, returns `view`
template <typename T>
GenericRectangle2D<T> CenterOnWhileInBounds(const GenericPoint2D<T>& point, const GenericRectangle2D<T>& view, const GenericRectangle2D<T>& bounds)
{
	if (!bounds.IsInside(point)) return view;
	
	GenericRectangle2D<T> moved = view.GetMovedTo(point.X-view.GetWidth(), point.Y-view.GetHeight());
	return bounds.GetRectangleMovedInside(moved);
}

///************************************************************************/
///* Coordinates and grids                                                */
///************************************************************************/

template <typename T>
GenericPoint2D<T> HexGridPositionToCoordinates(const GenericPoint2D<T>& hexPos, const GenericPoint2D<T>& hexSize, const GenericPoint2D<T>& offset)
{
	return hexPos; /// TODO: Finish me!
}

template <typename T, typename VISITOR/* = GTL::Common::NOPFunction*/>
uint32_t SelectGridTilesUnderSegment(const GenericSegment2D<T>& ray, VISITOR visitor)
{
	uint32_t result = 0;
	int dx = (int)fabs(ray.End.X - ray.Start.X);
	int dy = (int)fabs(ray.End.Y - ray.Start.Y);
	int x = (int)ray.Start.X;
	int y = (int)ray.Start.Y;
	int n = 1 + dx + dy;
	int x_inc = (ray.End.X > ray.Start.X) ? 1 : -1;
	int y_inc = (ray.End.Y > ray.Start.Y) ? 1 : -1;
	int error = dx - dy;
	dx *= 2;
	dy *= 2;

	for (; n > 0; --n)
	{
		result++;
		visitor(GenericPoint2D<T>(T(x), T(y)));

		if (error > 0)
		{
			x += x_inc;
			error -= dy;
		}
		else
		{
			y += y_inc;
			error += dx;
		}
	}
	return result;
}

///************************************************************************/
///* Typedefs                                                             */
///************************************************************************/

typedef GenericRectangle2D<int> GenericRectangle2DInteger;
typedef GenericRectangle2D<float> GenericRectangle2DFloat;

typedef GenericRay2D<int> GenericRay2DInteger;
typedef GenericRay2D<float> GenericRay2DFloat;
